/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.isfd.service.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.comac.ins.common.core.exception.base.BaseException;
import com.comac.ins.common.core.utils.MapstructUtils;
import com.comac.ins.common.core.utils.StringUtils;
import com.comac.ins.common.json.utils.JsonUtils;
import com.comac.ins.common.mybatis.core.page.PageQuery;
import com.comac.ins.common.mybatis.core.page.TableDataInfo;
import com.comac.ins.isfd.constant.IsfdMeshModelConstants;
import com.comac.ins.isfd.constant.enums.*;
import com.comac.ins.isfd.domain.IsfdMeshModelCollect;
import com.comac.ins.isfd.domain.bo.IsfdMeshModelBo;
import com.comac.ins.isfd.domain.bo.IsfdMeshModelCollectBo;
import com.comac.ins.isfd.domain.bo.IsfdMeshModelFilterRulesBo;
import com.comac.ins.isfd.domain.bo.IsfdMeshModelUploadConfirmBo;
import com.comac.ins.isfd.domain.bo.query.IsfdMeshModelCollectQueryBo;
import com.comac.ins.isfd.domain.vo.*;
import com.comac.ins.isfd.mapper.IsfdMeshModelCollectMapper;
import com.comac.ins.isfd.service.*;
import com.comac.ins.isfd.util.CompressUtils;
import com.comac.ins.isfd.util.FileUtils;
import com.comac.ins.isfd.util.RegexMatcher;
import com.comac.ins.isfd.util.TreeUtils;
import com.comac.ins.system.service.ISysDictDataService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;


/**
 * 自然网格模型库-采集Service业务层处理
 *
 * @author hxloongs
 * @date 2025-01-13
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class IsfdMeshModelCollectServiceImpl implements IIsfdMeshModelCollectService {

    @Autowired
    private IsfdMeshModelCollectMapper baseMapper;

    @Autowired
    private ISysDictDataService iSysDictDataService;

    @Autowired
    private IIsfdMeshModelStructureDefinitionService iIsfdMeshModelStructureDefinitionService;

    @Autowired
    private IIsfdCollectBatchHistoryService iIsfdCollectBatchHistoryService;

    @Autowired
    private IIsfdDatabaseDataManagementService isfdDatabaseDataManagementService;

    @Autowired
    private IIsfdMeshModelParseService iIsfdMeshModelParseService;


    @Autowired
    private RegexMatcher regexMatcher;

    @Autowired
    @Qualifier("bdfParseExecutor")
    private ExecutorService bdfParseExecutor;

    @Override
    public IsfdMeshModelCollectVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }


    @Override
    public TableDataInfo<IsfdMeshModelCollectVo> queryPageList(IsfdMeshModelCollectQueryBo queryBo) {
        LambdaQueryWrapper<IsfdMeshModelCollect> lqw = buildQueryWrapper(queryBo);
        PageQuery pageQuery = new PageQuery();
        pageQuery.setPageNum(queryBo.getPageNum());
        pageQuery.setPageSize(queryBo.getPageSize());
        Page<IsfdMeshModelCollectVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }


    @Override
    public List<IsfdMeshModelCollectVo> queryList(IsfdMeshModelCollectBo bo) {
        LambdaQueryWrapper<IsfdMeshModelCollect> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    @Override
    public IsfdMeshModelUploadDataHandlingVo uploadDataHandling(MultipartFile multipartFile, Long uploadTime, String modelNumber, String batchNumber) {
        IsfdMeshModelUploadDataHandlingVo result = new IsfdMeshModelUploadDataHandlingVo();
        // 1.解压
        // 压缩文件原名
        String compressFileName = multipartFile.getOriginalFilename();
        // 获取文件名
        String fileName = StringUtils.substringBeforeLast(compressFileName, ".");
        // 获取文件后缀
        String fileSuffix = StringUtils.substringAfterLast(compressFileName, ".");

        List<String> allPath = null;
        String dirPath = null;
        File file = null;
        try {
            String prefixPath = iSysDictDataService.selectDictValueByTypeAndLabel(IsfdMeshModelConstants.MODEL_MESH, IsfdMeshModelConstants.MODEL_MESH_DATA_PREFIX);
            if (StringUtils.isBlank(prefixPath)) {
                throw new BaseException("自然网格模型库上传地址前缀未配置，请到字典菜单栏配置该项。");
            }
            // 创建目录
            dirPath = prefixPath + modelNumber + "/" + batchNumber + "/" + uploadTime;
            File dir = new File(dirPath);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            // multipartFile 转file
            file = FileUtils.writeMultipartFileToDirectory(multipartFile, dirPath, compressFileName);
            if (fileSuffix.equalsIgnoreCase(CompressionTypeEnum.ZIP.getValue())) {
                allPath = CompressUtils.extractZipReturnPathList(file, dir, System.getProperty("sun.jnu.encoding"), Boolean.TRUE);
            } else if (fileSuffix.equalsIgnoreCase(CompressionTypeEnum.RAR.getValue())) {
                allPath = CompressUtils.extractRarReturnPathList(file, dir, Boolean.TRUE);
            } else {
                throw new BaseException("不支持该压缩文件类型，请确认是导入压缩包类型后再试。");
            }
            if (CollectionUtils.isEmpty(allPath)) {
                throw new BaseException("解压失败");
            }
        } catch (Exception e) {
            log.error("解压失败", e);
            if (e instanceof BaseException) {
                throw (BaseException) e;
            }
            throw new BaseException("解压失败");
        }

        // 2.封装数据列表
        List<IsfdMeshModelCollectVo> collectVoList = new ArrayList<>();
        try {
            // 压缩包
            IsfdMeshModelCollectVo compressedVo = buildVo(uploadTime, modelNumber, batchNumber, dirPath, fileName, fileSuffix,
                IsfdMeshModelFileTypeEnum.COMPRESSED, Boolean.FALSE, null, 0L, 0L);
            // 文件夹
            IsfdMeshModelCollectVo dirVo = buildVo(uploadTime, modelNumber, batchNumber, dirPath, fileName, null,
                IsfdMeshModelFileTypeEnum.DIR, Boolean.FALSE, null, 0L, compressedVo.getId());
            // P_load文件夹
            IsfdMeshModelCollectVo pLoadVo = buildVo(uploadTime, modelNumber, batchNumber, dirPath, IsfdMeshModelConstants.MODEL_MESH_P_LOAD, null,
                IsfdMeshModelFileTypeEnum.DIR, Boolean.TRUE, null, dirVo.getId(), compressedVo.getId());
            collectVoList.add(compressedVo);
            collectVoList.add(dirVo);
            collectVoList.add(pLoadVo);
            for (String pathStr : allPath) {
                Long parentId = dirVo.getId();
                if (pathStr.toLowerCase().contains(IsfdMeshModelConstants.MODEL_MESH_P_LOAD_IGNORE_CASE)) {
                    parentId = pLoadVo.getId();
                }
                String[] fileNameAndSuffix = FileUtils.getFileNameAndSuffix(pathStr);
                IsfdMeshModelCollectVo collectVo = buildVo(uploadTime, modelNumber, batchNumber, null, fileNameAndSuffix[0], fileNameAndSuffix[1],
                    IsfdMeshModelFileTypeEnum.FILE, Boolean.FALSE, pathStr, parentId, compressedVo.getId());
                collectVoList.add(collectVo);
            }
        } catch (Exception e) {
            log.error("封装数据对象报错", e);
            throw new BaseException("封装数据对象报错");
        }
        result.setCollectVoList(collectVoList);
        // 3.封装左侧树
        List<String> pLoadPathList = allPath.stream().filter(path -> path.toLowerCase().contains(IsfdMeshModelConstants.MODEL_MESH_P_LOAD_IGNORE_CASE)).toList();
        List<IsfdMeshModelStructureDefinitionTreeNodeVo> treeNodeVos = iIsfdMeshModelStructureDefinitionService.listTree(modelNumber, batchNumber,
            CollectionUtils.isEmpty(pLoadPathList) ? Boolean.FALSE : Boolean.TRUE);
        result.setTreeNodeVoList(treeNodeVos);
        // 4.统计对象
        IsfdMeshModelCountVo countVo = new IsfdMeshModelCountVo();
        countVo.setTotalCount((long) allPath.size());
        countVo.setFilterCount(collectVoList.stream().filter(c -> IsfdMeshModelFileTypeEnum.FILE.getValue().equals(c.getFileType()) &&
            IsfdMeshModelFilteredEnum.FILTERED.getValue().equals(c.getFiltered())).count());
        countVo.setNotFilterCount(collectVoList.stream().filter(c -> IsfdMeshModelFileTypeEnum.FILE.getValue().equals(c.getFileType()) &&
            IsfdMeshModelFilteredEnum.NOT_FILTERED.getValue().equals(c.getFiltered())).count());
        result.setCountVo(countVo);
        return result;
    }

    private IsfdMeshModelCollectVo buildVo(Long uploadTime, String modelNumber, String batchNumber,
                                           String dirPath, String fileName, String fileSuffix,
                                           IsfdMeshModelFileTypeEnum isfdMeshModelFileTypeEnum, Boolean pLoad, String tempPathStr,
                                           Long parentId, Long topParentId) throws Exception {
        String pathStr = null;
        String fullFileName = StringUtils.isNotBlank(fileSuffix) ? fileName + "." + fileSuffix : fileName;
        Long fileSize = 0L;
        Long id = IdUtil.getSnowflakeNextId();
        String sectionNumber = null;
        String sectionName = null;
        String childSectionNumber = null;
        String childSectionName = null;
        Integer filtered = null;
        Path path = null;
        if (IsfdMeshModelFileTypeEnum.COMPRESSED.getValue().equals(isfdMeshModelFileTypeEnum.getValue())) {
            filtered = IsfdMeshModelFilteredEnum.NOT_FILTERED.getValue();
            pathStr = FileUtils.buildFilePath(dirPath, fullFileName);
            topParentId = id;
            path = Paths.get(pathStr);
            fileSize = Files.size(path);
        } else if (IsfdMeshModelFileTypeEnum.DIR.getValue().equals(isfdMeshModelFileTypeEnum.getValue())) {
            filtered = IsfdMeshModelFilteredEnum.NOT_FILTERED.getValue();
            if (pLoad) {
                pathStr = FileUtils.buildFilePath(dirPath, fileName, IsfdMeshModelConstants.MODEL_MESH_P_LOAD);
            } else {
                pathStr = FileUtils.buildFilePath(dirPath, fileName);
            }
            path = Paths.get(pathStr);
        } else if (IsfdMeshModelFileTypeEnum.FILE.getValue().equals(isfdMeshModelFileTypeEnum.getValue())) {
            IsfdMeshModelFilterRulesBo rulesResult = regexMatcher.meshModelFilterRules(modelNumber, fileName, fileSuffix);
            filtered = rulesResult.getFiltered();
            sectionNumber = rulesResult.getSectionNumber();
            sectionName = rulesResult.getSectionName();
            pathStr = tempPathStr;
            path = Paths.get(pathStr);
            fileSize = Files.size(path);
        } else {
            throw new BaseException("不支持的文件类型");
        }
        IsfdMeshModelCollectVo collectVo = new IsfdMeshModelCollectVo();
        collectVo.setUploadTime(uploadTime);
        collectVo.setFullFileName(fullFileName);
        collectVo.setFileName(fileName);
        collectVo.setFileSuffix(fileSuffix);
        collectVo.setFileUrl(path.toAbsolutePath().toString());
        collectVo.setFileSize(fileSize);
        collectVo.setModelNumber(modelNumber);
        collectVo.setBatchNumber(batchNumber);
        collectVo.setFileType(isfdMeshModelFileTypeEnum.getValue());
        collectVo.setId(id);
        collectVo.setParentId(parentId);
        collectVo.setTopParentId(topParentId);
        collectVo.setSectionNumber(sectionNumber);
        collectVo.setSectionName(sectionName);
        collectVo.setChildSectionNumber(childSectionNumber);
        collectVo.setChildSectionName(childSectionName);
        collectVo.setFiltered(filtered);
        collectVo.setQualityCheck(0L);
        return collectVo;
    }


    @Override
    @Transactional
    public IsfdMeshModelUploadConfirmVo uploadConfirm(IsfdMeshModelUploadConfirmBo confirmBo) {
        String tableName = confirmBo.getTableName();
        List<IsfdMeshModelCollectBo> collectBoList = confirmBo.getBoList();

        String modelNumber = collectBoList.get(0).getModelNumber();
        String batchNumber = collectBoList.get(0).getBatchNumber();
        IsfdMeshModelUploadConfirmVo result = new IsfdMeshModelUploadConfirmVo();

        // 1.调用建模引擎工具导入数据
        List<IsfdMeshModelBo> meshModelBoList = collectBoList.stream()
            .filter(bo -> IsfdMeshModelFileTypeEnum.FILE.getValue().equals(bo.getFileType()) && IsfdMeshModelFilteredEnum.NOT_FILTERED.getValue().equals(bo.getFiltered())
                || IsfdMeshModelConstants.OTHER.equals(bo.getSectionNumber()))
            .map(bo -> MapstructUtils.convert(bo, IsfdMeshModelBo.class))
            .toList();

        // 防止bdf/out/dat 以外的数据入库
        meshModelBoList = meshModelBoList.stream().filter(bo -> IsfdModelMeshFileSuffixEnum.forValue(bo.getFileSuffix()) != null).toList();

        if(CollectionUtils.isEmpty(meshModelBoList)){
            throw new BaseException("经过校验，无满足入库的文件，请确认文件类型后再试！");
        }

        List<Map<String, Object>> entityListJson = JsonUtils.parseJsonStrToListMap(JsonUtils.toJsonString(meshModelBoList));
        // 生成_id主键
        List<String> entityIds = new ArrayList<>();
        for (Map<String, Object> data : entityListJson) {
            String objectIdStr = new ObjectId().toHexString();
            data.put(IsfdDatabaseModelSpecialFieldNameEnum.OBJECT_ID.getValue(), objectIdStr);
            entityIds.add(objectIdStr);
        }

        isfdDatabaseDataManagementService.insertOrUpdateDataList(tableName, entityListJson, IsfdImportMethodEnum.DIRECTLY, null, false);

        // 2.保存采集数据到采集表
        List<IsfdMeshModelCollect> collectPoList = MapstructUtils.convert(collectBoList, IsfdMeshModelCollect.class);
        baseMapper.insertBatch(collectPoList);
        List<IsfdMeshModelCollectVo> collectVoList = MapstructUtils.convert(collectPoList, IsfdMeshModelCollectVo.class);

        // 3.生成机型批次记录
        iIsfdCollectBatchHistoryService.save(modelNumber, batchNumber);
        // 4.封装返回
        // 统计数据
        List<IsfdMeshModelCollectVo> collectFileVoList = collectVoList.stream().filter(c -> IsfdMeshModelFileTypeEnum.FILE.getValue().equals(c.getFileType())).toList();
        IsfdMeshModelCountVo countVo = new IsfdMeshModelCountVo();
        countVo.setTotalCount((long) collectFileVoList.size());
        countVo.setFilterCount(collectFileVoList.stream().filter(c -> IsfdMeshModelFilteredEnum.FILTERED.getValue().equals(c.getFiltered())).count());
        countVo.setNotFilterCount(collectFileVoList.stream().filter(c -> IsfdMeshModelFilteredEnum.NOT_FILTERED.getValue().equals(c.getFiltered())).count());
        result.setCountVo(countVo);
        // 详情数据-树结构
        List<IsfdMeshModelCollectVo> preTreeList = collectVoList.stream().filter(c -> IsfdMeshModelFilteredEnum.NOT_FILTERED.getValue().equals(c.getFiltered())).toList();
        IsfdMeshModelCollectTreeVo treeVo = buildFileTree(preTreeList);
        result.setTreeVo(treeVo);

        // todo 启用一个线程进行结构化
        // 查询开关，如果定时器任务的开关为false，那么就进行异步线程的结构化
        // 入参是objectId列表
//        LoginUser loginUser = LoginHelper.getLoginUser();
//        boolean executeJob = Boolean.parseBoolean(iSysDictDataService.selectDictValueByTypeAndLabel(IsfdMeshModelConstants.MODEL_MESH, IsfdMeshModelConstants.MESH_JOB_STRUCTURED_SWITCH));
//        if (!executeJob) {
//            try {
//                CompletableFuture.runAsync(() -> {
//                    log.info("执行异步BDF结构化任务");
//                    iIsfdMeshModelParseService.executeParse(entityIds, loginUser);
//                }, bdfParseExecutor);
//            } catch (Exception e) {
//                log.error("BDF结构化异步调度任务失败：", e);
//            }
//
//        }

        return result;
    }

    @Override
    public IsfdMeshModelCollectTreeVo allFiles(Long id) {
        List<IsfdMeshModelCollectVo> collectVoList = baseMapper.selectVoList(new LambdaQueryWrapper<IsfdMeshModelCollect>()
            .eq(IsfdMeshModelCollect::getTopParentId, id)
            .eq(IsfdMeshModelCollect::getFiltered, IsfdMeshModelFilteredEnum.NOT_FILTERED.getValue()));
        if (CollectionUtils.isEmpty(collectVoList)) {
            throw new BaseException("查询的文件不存在，请确认后重试！");
        }
        // 详情数据-树结构
        return buildFileTree(collectVoList);
    }

    /**
     * 详情数据-树结构
     */
    private IsfdMeshModelCollectTreeVo buildFileTree(List<IsfdMeshModelCollectVo> collectVoList) {
        String modelNumber = collectVoList.get(0).getModelNumber();
        List<IsfdMeshModelCollectTreeVo> treeVoList = new ArrayList<>();
        for (IsfdMeshModelCollectVo collectVo : collectVoList) {
            String sectionNumber = collectVo.getSectionNumber();
            String childSectionNumber = collectVo.getChildSectionNumber();
            IsfdMeshModelStructureDefinitionVo sectionVo = iIsfdMeshModelStructureDefinitionService.queryOneByQueryIndex(modelNumber, sectionNumber, null);
            if (StringUtils.isNotBlank(sectionNumber) && sectionVo != null) {
                IsfdMeshModelStructureDefinitionVo childSectionVo = iIsfdMeshModelStructureDefinitionService.queryOneByQueryIndex(modelNumber, sectionNumber, childSectionNumber);
                if (StringUtils.isNotBlank(childSectionNumber) && childSectionVo != null) {
                    collectVo.setChildSectionName(childSectionVo.getName());
                }
                collectVo.setSectionName(sectionVo.getName());
            }
            treeVoList.add(MapstructUtils.convert(collectVo, IsfdMeshModelCollectTreeVo.class));
        }
        List<IsfdMeshModelCollectTreeVo> preTreeVoList = treeVoList.stream().filter(t -> !IsfdMeshModelFileTypeEnum.COMPRESSED.getValue().equals(t.getFileType())).toList();
        return TreeUtils.buildTree(preTreeVoList);
    }

    private LambdaQueryWrapper<IsfdMeshModelCollect> buildQueryWrapper(IsfdMeshModelCollectQueryBo bo) {
        LambdaQueryWrapper<IsfdMeshModelCollect> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getFileName()), IsfdMeshModelCollect::getFileName, bo.getFileName());
        lqw.eq(StringUtils.isNotBlank(bo.getModelNumber()), IsfdMeshModelCollect::getModelNumber, bo.getModelNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getBatchNumber()), IsfdMeshModelCollect::getBatchNumber, bo.getBatchNumber());
        lqw.eq(bo.getQualityCheck() != null, IsfdMeshModelCollect::getQualityCheck, bo.getQualityCheck());
        if (bo.getUploadTimeStartTime() != null && bo.getUploadTimeEndTime() != null) {
            lqw.between(IsfdMeshModelCollect::getUploadTime, bo.getUploadTimeStartTime().getTime(), bo.getUploadTimeEndTime().getTime());
        }
        if (StringUtils.isNotBlank(bo.getFileSuffix())) {
            lqw.eq(IsfdMeshModelCollect::getFileSuffix, bo.getFileSuffix());
        } else {
            lqw.in(IsfdMeshModelCollect::getFileSuffix, IsfdMeshModelConstants.COMPRESSED_LIST);
        }
        lqw.orderByDesc(IsfdMeshModelCollect::getUploadTime);
        return lqw;
    }

    private LambdaQueryWrapper<IsfdMeshModelCollect> buildQueryWrapper(IsfdMeshModelCollectBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<IsfdMeshModelCollect> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getParentId() != null, IsfdMeshModelCollect::getParentId, bo.getParentId());
        lqw.eq(bo.getTopParentId() != null, IsfdMeshModelCollect::getTopParentId, bo.getTopParentId());
        lqw.eq(bo.getUploadTime() != null, IsfdMeshModelCollect::getUploadTime, bo.getUploadTime());
        lqw.like(StringUtils.isNotBlank(bo.getFileName()), IsfdMeshModelCollect::getFileName, bo.getFileName());
        lqw.eq(bo.getFileSize() != null, IsfdMeshModelCollect::getFileSize, bo.getFileSize());
        lqw.eq(StringUtils.isNotBlank(bo.getFileType()), IsfdMeshModelCollect::getFileType, bo.getFileType());
        lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), IsfdMeshModelCollect::getFileSuffix, bo.getFileSuffix());
        lqw.eq(StringUtils.isNotBlank(bo.getFileUrl()), IsfdMeshModelCollect::getFileUrl, bo.getFileUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getModelNumber()), IsfdMeshModelCollect::getModelNumber, bo.getModelNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getBatchNumber()), IsfdMeshModelCollect::getBatchNumber, bo.getBatchNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getSectionNumber()), IsfdMeshModelCollect::getSectionNumber, bo.getSectionNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getChildSectionNumber()), IsfdMeshModelCollect::getChildSectionNumber, bo.getChildSectionNumber());
        lqw.eq(bo.getQualityCheck() != null, IsfdMeshModelCollect::getQualityCheck, bo.getQualityCheck());
        return lqw;
    }

}
